package com.ww.springboot.boot.util;

import cn.hutool.core.util.ObjectUtil;
import com.ww.springboot.boot.algorithm.stack.StackSelf;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;

/**
 * 描述：
 *
 * @author wanwei
 * @since 2021-07-29 14:15
 */
public class CalculatorUtil {


    private static Map<String, Integer> map = new HashMap<String, Integer>() {
        {
            put("(", 0);
            put(")", 0);
            put("*", 20);
            put("^", 30);
            put("|", 30);
            put("&", 30);
            put("/", 20);
            put("%", 20);
            put("+", 10);
            put("-", 10);
        }
    };

    /**
     * 后缀表达式 :数字则入栈 遇到运算符则出栈 并计算
     *
     * @param content
     * @return
     */
    public static Double count(String content) {
        System.out.println(content);
        Stack<String> stack = new Stack();
        String[] array = content.split(",");
        for (int i = 0; i < array.length; i++) {
            String str = array[i];
            if (isDigit(str)) {
                stack.push(str);
                continue;
            }
            String one = stack.pop();
            String two = stack.pop();
            Double result;
            switch (str) {
                case "+":
                    result = Double.valueOf(two) + Double.valueOf(one);
                    stack.push(new BigDecimal(result).toString());
                    break;
                case "-":
                    result = Double.valueOf(two) - Double.valueOf(one);
                    stack.push(new BigDecimal(result).toString());
                    break;
                case "*":
                    result = Double.valueOf(two) * Double.valueOf(one);
                    stack.push(new BigDecimal(result).toString());
                    break;
                case "/":
                    if (Double.valueOf(two) == 0) {
                        throw new RuntimeException("除数不能为0");
                    }
                    result = Double.valueOf(two) / Double.valueOf(one);
                    stack.push(new BigDecimal(result).toString());
                    break;
                case "%":
                    if (Long.valueOf(two) == 0) {
                        throw new RuntimeException("除数不能为0");
                    }
                    result = Double.valueOf(two) % Double.valueOf(one);
                    stack.push(new BigDecimal(result).toString());
                    break;
                case "^":
                    result = Double.valueOf(Integer.valueOf(two) ^ Integer.valueOf(one));
                    stack.push(new BigDecimal(result).toString());
                    break;
                case "|":
                    result = Double.valueOf(Integer.valueOf(two) | Integer.valueOf(one));
                    stack.push(new BigDecimal(result).toString());
                    break;
                case "&":
                    result = Double.valueOf(Integer.valueOf(two) & Integer.valueOf(one));
                    stack.push(new BigDecimal(result).toString());
                    break;
                case "(":
                    break;
                case ")":
                    break;
                default:
                    break;
            }
        }
        return Double.valueOf(stack.pop());
    }

    /**
     * 中缀转后缀表达式
     * 1.数字直接输出
     * 2.运算符 判断是否大于栈顶元素 大于直接入栈
     * 小于等于则将栈顶元素出栈 直到栈顶元素小于运算符或者栈空
     * 3.遇到 括号 (直接入栈  )则将之前的运算符顺序出栈 直到遇到(
     *
     * @param str
     * @return
     */
    public static String changeToSuffix(String str) {
        StringBuilder sb = new StringBuilder();
        String s = new String();
        Stack<String> stack = new Stack();
        for (int i = 0; i < str.length(); i++) {
            if (str.charAt(i) == ' ') {
                //空格直接丢弃
                continue;
            }
            //兼容负数与小数
            if ((str.charAt(i) == '-' && s.length() == 0)
                    || (Character.isDigit(str.charAt(i)) || str.charAt(i) == '.')) {
                s += str.charAt(i);
            } else {
                if (ObjectUtil.isNotEmpty(s)) {
                    sb.append(s).append(",");
                    s = "";
                }
                String chStr = String.valueOf(str.charAt(i));
                deal(sb, stack, chStr);
            }
            if (i == str.length() - 1) {
                if (ObjectUtil.isNotEmpty(s)) {
                    sb.append(s).append(",");
                    s = "";
                }
                while (!stack.isEmpty()) {
                    sb.append(stack.pop()).append(",");
                }
            }
        }
        return sb.substring(0, sb.length() - 1);
    }

    private static void deal(StringBuilder sb, Stack<String> stack, String chStr) {
        //运算符 根据栈顶元素判断
        if (stack.isEmpty()) {
            //空 直接入栈
            stack.push(chStr);
        } else {
            String chPop = stack.peek();
            if ("(".equals(chStr)) {
                stack.push(chStr);
                return;
            }
            if ("(".equals(chPop)
                    && ")".equals(chStr)) {
                //表示()相遇 将（出栈即可
                stack.pop();
                return;
            }
            int result = compare(chStr, chPop);
            if (result == -1) {
                //-1 小于 将栈内元素出栈 并重复比较逻辑
                sb.append(stack.pop()).append(",");
                deal(sb, stack, chStr);
            } else if (result == 1) {
                stack.push(chStr);
            }
        }
    }

    private static Integer compare(String str1, String str2) {
        if (map.get(str1) > map.get(str2)) {
            //优先级大于
            return 1;
        } else {
            //优先级不大于
            return -1;
        }
    }

    private static Boolean isDigit(String str) {
        if("-".equals(str)){
            return false;
        }
        for (int i = 0; i < str.length(); i++) {
            //兼容小数
            if(str.charAt(0) == '-'){
                continue;
            }
            if (!Character.isDigit(str.charAt(i))
                    && str.charAt(i) != '.') {
                return false;
            }
        }
        return true;
    }
}
